/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#ifndef MC_ipmb_H
#define MC_ipmb_H

#include <mc/common.h>
#include <mc/dict.h>
#include <mc/engine/service.h>
#include <mc/variant.h>
#include <optional>
#include <stdint.h>
#include <string>
#include <vector>

namespace mc {

// IPMI 网络功能码定义
enum class ipmi_net_fn : uint8_t {
    chassis   = 0x00,
    bridge    = 0x02,
    sensor    = 0x04,
    app       = 0x06,
    firmware  = 0x08,
    storage   = 0x0A,
    transport = 0x0C,
    group_ext = 0x2C,
    oem       = 0x2E,
};

// IPMI 完成码定义
enum class ipmi_completion_code : uint8_t {
    success                 = 0x00,
    node_busy               = 0xC0,
    invalid_command         = 0xC1,
    invalid_command_for_lun = 0xC2,
    timeout                 = 0xC3,
    out_of_space            = 0xC4,
    invalid_reservation     = 0xC5,
    data_truncated          = 0xC6,
    invalid_length          = 0xC7,
    length_exceeded         = 0xC8,
    parameter_out_of_range  = 0xC9,
    cannot_return_data      = 0xCA,
    req_data_not_present    = 0xCB,
    invalid_data_field      = 0xCC,
    command_illegal         = 0xCD,
    response_not_provided   = 0xCE,
    duplicated_request      = 0xCF,
    unknown_error           = 0xFF,
};

// IPMB 通道类型
enum class ipmb_channel_type : uint8_t {
    smm    = 0x00,
    me     = 0x01,
    mmc    = 0x02,
    host   = 0x03,
    lan    = 0x04,
    base   = 0x05,
    rabric = 0x06,
    fcoe   = 0x07,
    bmc    = 0x08,
    edma_0 = 0x09,
};

// IPMB 地址信息
struct ipmb_address {
    uint8_t slave_addr; // 从地址
    uint8_t lun;        // 逻辑单元号
};

// IPMI 请求结构体
struct ipmi_request {
    ipmi_net_fn          net_fn;            // 网络功能码
    uint8_t              cmd;               // 命令码
    std::vector<uint8_t> data;              // 请求数据
    uint32_t             timeout_ms = 5000; // 超时时间(毫秒)
};

// IPMI 响应结构体
struct ipmi_response {
    ipmi_completion_code completion_code; // 完成码
    std::vector<uint8_t> data;            // 响应数据
};

// 定义资源树路径常量
constexpr std::string_view IPMI_CORE_SERVICE   = "bmc.kepler.ipmi_core";
constexpr std::string_view IPMI_CORE_PATH      = "/bmc/kepler/IpmiCore";
constexpr std::string_view IPMI_CORE_INTERFACE = "bmc.kepler.IpmiCore";

class MC_API ipmb {
public:
    explicit ipmb(mc::engine::service* service);

    ~ipmb();

    std::optional<ipmi_response> send_request(ipmb_channel_type channel_type, ipmi_net_fn net_fn,
                                              uint8_t cmd, const std::vector<uint8_t>& data = {},
                                              uint32_t timeout_ms = 5000);

    std::optional<ipmi_response> send_request(ipmb_channel_type   channel_type,
                                              const ipmi_request& request);

    void set_retry_count(int retry_count);

    int get_retry_count() const;

    std::optional<ipmi_response> parse_response(const mc::variant& completion_code,
                                                const mc::variant& response_data);

private:
    std::optional<ipmi_response> send_to_ipmi_core(ipmb_channel_type   channel_type,
                                                   const ipmi_request& request);

    template <typename T, typename Operation>
    std::optional<T> retry_operation(Operation operation);

private:
    mc::engine::service* m_service;
    int                  m_retry_count;

    static constexpr uint8_t DEFAULT_INSTANCE = 0x00;
    static constexpr uint8_t DEFAULT_LUN      = 0x00;
};

namespace ipmi_utils {

MC_API std::string_view format_completion_code(ipmi_completion_code code);

constexpr bool is_success(ipmi_completion_code code) {
    return code == ipmi_completion_code::success;
}

constexpr bool is_retryable(ipmi_completion_code code) {
    return code == ipmi_completion_code::node_busy || code == ipmi_completion_code::timeout;
}

} // namespace ipmi_utils

} // namespace mc

#endif // MC_ipmb_H
